package org.luxor.commons.redis.config;

import org.luxor.commons.redis.RedisClient;
import org.luxor.commons.redis.aspect.*;
import org.luxor.commons.redis.client.*;
import org.luxor.commons.redis.component.RedisListenerReceiver;
import org.luxor.commons.redis.plugin.idempotent.service.IdempotentService;
import org.luxor.commons.redis.plugin.idempotent.service.impl.IdempotentServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * Redis配置
 *
 * @author chenshun
 * @email sunlightcs@gmail.com
 * @date 2017-07-70 19:22
 */
@Configuration
@EnableCaching
@Import(RedisListenerRegistrar.class)
@Order(998)
public class RedisConfig {

    /**
     * 用于spring session，防止每次创建一个线程
     */
    @Bean
    @ConditionalOnMissingBean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(8);
        threadPoolTaskExecutor.setMaxPoolSize(64);
        threadPoolTaskExecutor.setKeepAliveSeconds(10);
        threadPoolTaskExecutor.setQueueCapacity(1000);
        threadPoolTaskExecutor.setThreadNamePrefix("Task-");
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

    @Bean
    @ConditionalOnMissingBean
    public MessageListenerAdapter messageListenerAdapter() {
        MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(new RedisListenerReceiver() {
            @Override
            public void onMessage(String message, String channel) {
                // .ignore
            }
        }, "onMessage");
        return messageListenerAdapter;
    }

    /**
     * Redis消息监听器容器
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass({RedisConnectionFactory.class, ThreadPoolTaskExecutor.class, MessageListenerAdapter.class})
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                                   ThreadPoolTaskExecutor threadPoolTaskExecutor,
                                                   MessageListenerAdapter messageListenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.setTaskExecutor(threadPoolTaskExecutor);
        container.setSubscriptionExecutor(threadPoolTaskExecutor);
        container.addMessageListener(messageListenerAdapter, new PatternTopic("topic.*"));
        return container;
    }

    @Bean
    @ConditionalOnMissingBean
    public GuavaCacheManager cacheManager() {
        return new GuavaCacheManager();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RedisObjectClient.class)
    public CacheableAspect cacheableAspect(RedisObjectClient redisClientObject) {
        CacheableAspect cacheableAspect = new CacheableAspect();
        cacheableAspect.setRedisClientObject(redisClientObject);
        return cacheableAspect;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RedisObjectClient.class)
    public CachePutAspect cachePutAspect(RedisObjectClient redisClientObject) {
        CachePutAspect cachePutAspect = new CachePutAspect();
        cachePutAspect.setRedisClientObject(redisClientObject);
        return cachePutAspect;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RedisObjectClient.class)
    public CacheEvictAspect cacheEvictAspect(RedisObjectClient redisClientObject) {
        CacheEvictAspect cacheEvictAspect = new CacheEvictAspect();
        cacheEvictAspect.setRedisClientObject(redisClientObject);
        return cacheEvictAspect;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass({RedisZSetClient.class})
    public LimitRateAspect limitRateAspect(RedisZSetClient redisZSetClient) {
        LimitRateAspect limitRateAspect = new LimitRateAspect();
        limitRateAspect.setRedisClientZset(redisZSetClient);
        return limitRateAspect;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass({RedisStringClient.class})
    public IdempotentAspect idempotentAspect(RedisStringClient redisStringClient) {
        IdempotentAspect idempotentAspect = new IdempotentAspect();
        idempotentAspect.setRedisClientString(redisStringClient);
        return idempotentAspect;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass({RedisStringClient.class})
    public IdempotentService idempotentService(RedisStringClient redisStringClient) {
        IdempotentService idempotentService = new IdempotentServiceImpl(redisStringClient);
        return idempotentService;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RedisConnectionFactory.class)
    protected StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(StringRedisTemplate.class)
    public RedisStringClient redisStringClient(StringRedisTemplate stringRedisTemplate) {
        RedisStringClient redisClientString = new RedisStringClient();
        redisClientString.setRedisTemplate(stringRedisTemplate);
        return redisClientString;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass({StringRedisTemplate.class, ValueOperations.class})
    public RedisObjectClient redisObjectClient(StringRedisTemplate stringRedisTemplate, ValueOperations valueOperations) {
        RedisObjectClient redisClientObject = new RedisObjectClient();
        redisClientObject.setRedisTemplate(stringRedisTemplate);
        redisClientObject.setValueOperations(valueOperations);
        return redisClientObject;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(StringRedisTemplate.class)
    public RedisHashClient redisHashClient(StringRedisTemplate stringRedisTemplate) {
        RedisHashClient redisClientHash = new RedisHashClient();
        redisClientHash.setRedisTemplate(stringRedisTemplate);
        return redisClientHash;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(StringRedisTemplate.class)
    public RedisListClient redisListClient(StringRedisTemplate stringRedisTemplate) {
        RedisListClient redisClientList = new RedisListClient();
        redisClientList.setRedisTemplate(stringRedisTemplate);
        return redisClientList;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(StringRedisTemplate.class)
    public RedisSetClient redisSetClient(StringRedisTemplate stringRedisTemplate) {
        RedisSetClient redisClientSet = new RedisSetClient();
        redisClientSet.setRedisTemplate(stringRedisTemplate);
        return redisClientSet;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(StringRedisTemplate.class)
    public RedisZSetClient redisZSetClient(StringRedisTemplate stringRedisTemplate) {
        RedisZSetClient redisClientZSet = new RedisZSetClient();
        redisClientZSet.setRedisTemplate(stringRedisTemplate);
        return redisClientZSet;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass({StringRedisTemplate.class, RedisMessageListenerContainer.class})
    public RedisTopicClient redisTopicClient(StringRedisTemplate redisTemplate, RedisMessageListenerContainer container) {
        RedisTopicClient redisClientTopic = new RedisTopicClient(redisTemplate, container);
        return redisClientTopic;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(StringRedisTemplate.class)
    public RedisQueueClient redisQueueClient(StringRedisTemplate stringRedisTemplate) {
        RedisQueueClient redisClientQueue = new RedisQueueClient();
        redisClientQueue.setRedisTemplate(stringRedisTemplate);
        return redisClientQueue;
    }

    @Bean
    @Primary
    @ConditionalOnMissingBean
    public RedisClient redisClient() {
        return new RedisClient();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RedisTemplate.class)
    public HashOperations<Object, String, Object> hashOperations(RedisTemplate<Object, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RedisTemplate.class)
    public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RedisTemplate.class)
    public ListOperations<Object, Object> listOperations(RedisTemplate<Object, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RedisTemplate.class)
    public SetOperations<Object, Object> setOperations(RedisTemplate<Object, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RedisTemplate.class)
    public ZSetOperations<Object, Object> zSetOperations(RedisTemplate<Object, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }


}